#include "tcp_server.h"
#include "lock_guard.h"
#include "ref_object.h"
#include "itcp_server.h"

#include <assert.h>
#include <errno.h>
#include <sys/socket.h>

namespace tinySrv
{

  TCPServer::TCPServer(std::string const &localAddress, unsigned short localPort, int backlog,
                       int maxConnections, int workingThreadCount,
                       std::auto_ptr<IClientFactory> clientsFactory,
                       int temporaryBufferSizeForRecv)
    : TCPSrvSocket(localAddress, localPort, backlog)
    , ProcessingQueue(workingThreadCount, this)
    , EPollSelector(GetSocket(), maxConnections)
    , Thread(Thread::ICallbackPtr(new ICallbackImpl<TCPServer>(*this, &TCPServer::ListenerFunc)))
    , IsRun(true)
    , MaxConnectionCount(maxConnections)
    , TemporaryBufferSize(temporaryBufferSizeForRecv)
    , ClientsFactory(clientsFactory)
  {
  }
  
  TCPServer::~TCPServer()
  {
    IsRun = false;
  }
  
  void TCPServer::ListenerFunc()
  {
    while (IsRun)
    {
      try
      {
        Select(SelectorWaitTimeout, this);
      }
      catch (std::exception const &)
      {
        // TODO: log
      }
    }
  }
  
  bool TCPServer::ProcessSocket(int sock, SelectType type, void *data)
  {
    try
    {
      switch (type)
      {
      case stOnConnect :
        OnConnect(sock);
        break;
      case stOnClose :
        OnCloseConnection(reinterpret_cast<ClientItemBase *>(data));
        break;
      case stOnRead :
        OnRecvData(reinterpret_cast<ClientItemBase *>(data));
        break;
      default :
        break;
      }
    }
    catch (std::exception const &)
    {
      // TODO: log
    }
    return IsRun;
  }
  
  void TCPServer::OnConnect(int sock)
  {
    TCPSocketHolder ClientSocket(accept4(sock, 0, 0, SOCK_NONBLOCK));
    ClientSocket.SetNoDelay(false);
    if (static_cast<int>(Clients.size()) >= MaxConnectionCount)
      return;
    ClientItemBasePtr ClientItem(RefObject<ClientItemBase>::Create());
    AddNewSocket(ClientSocket.GetSocket(), ClientItem.Get());
    assert(!!ClientsFactory.get());
    std::auto_ptr<IClient> UserClient = ClientsFactory->CreateClinetItem();
    if (!UserClient->Init(ClientItem.Get()))
      return;
    ClientItem->SetSocket(ClientSocket.Detach());
    ClientItem->SetUser(UserClient);
    Clients[ClientItem.Get()] = ClientItem;
  }
  
  void TCPServer::OnCloseConnection(ClientItemBase *client)
  {
    client->MarkForClose();
    client->OnClose();
    (TCPSocketHolder(client->GetSocket()));
    ClientsPool::iterator Iter = Clients.find(client);
    assert(Iter != Clients.end());
    if (Iter != Clients.end())
      Clients.erase(Iter);
  }
  
  void TCPServer::OnRecvData(ClientItemBase *client)
  {
    if (client->CanClose())
    {
      OnCloseConnection(client);
      return;
    }
    ClientsPool::iterator iter = Clients.find(client);
    assert(iter != Clients.end());
    if (iter != Clients.end())
      Push(iter->second);
  }
  
  void TCPServer::ProcessItem(IProcessingQueueCallback::IRefObjectPtr item)
  {
    assert(!!item.Get());
    ClientItemBase *Client = dynamic_cast<ClientItemBase *>(item.Get());
    assert(!!Client);
    if (!Client)
      return;
    ClientItemBase::CharVector Buffer(TemporaryBufferSize);
    for ( ; ; )
    {
      int Ret = recv(Client->GetSocket(), &Buffer[0], TemporaryBufferSize, 0);
      if (!Ret)
        return;
      if (Ret == -1)
      {
        if (errno != EWOULDBLOCK)
          Client->MarkForClose();
        else
        {
          //Push(item);
        }
        return;
      }
      if (!Client->CanClose())
        Client->OnData(&Buffer[0], Ret);
    }
  }

  class TCPServerWrapper
    : private NonCopyable
    , public ITCPServer
  {
  public:
    virtual void Start(std::string const &localAddress, unsigned short localPort, int backlog,
                       int maxConnections, int workingThreadCount,
                       std::auto_ptr<IClientFactory> clientsFactory,
                       int temporaryBufferSizeForRecv = 4096)
    {
      Server.reset(new TCPServer(localAddress, localPort, backlog,
                                 maxConnections, workingThreadCount,
                                 clientsFactory, temporaryBufferSizeForRecv));
    }
    virtual void Stop()
    {
      Server.reset(0);
    }
  private:
    std::auto_ptr<TCPServer> Server;
  };
  
  std::auto_ptr<ITCPServer> CreateTCPServer()
  {
    return std::auto_ptr<ITCPServer>(new TCPServerWrapper);
  }
  
}

